﻿using Digitalmes.Application;
using Digitalmes.Common.Result;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace Digitalmes.ApiService.Filters;

/// <summary>
/// 方法过滤器
/// </summary>
public sealed class AopActionFilter : IAsyncActionFilter
{
    private readonly ILogger _logger;

    public AopActionFilter(ILogger<AopActionFilter> logger)
    {
        _logger = logger;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // 验证实体
        if (!context.ModelState.IsValid)
        {
            var message = string.Join(" | ", context.ModelState.Values
                .SelectMany(v => v.Errors)
                .Select(e => e.ErrorMessage));

            _logger.LogInformation("参数校验失败, Api: {Api}, 错误参数：{Message}", context.HttpContext.Request.Path, message);
            context.Result = new JsonResult(JResult.Error("参数校验失败"));
            return;
        }

        // 计算执行耗时
        var stopwatch = Stopwatch.StartNew();
        var actionResult = await next();
        stopwatch.Stop();

        // 读取返回类型以及数据
        var (isObject, actionData) = CheckResult(actionResult.Result);

        // 返回统一格式
        if (isObject && !SkipJsonResult(context))
        {
            /// {
            ///     "code": 200,
            ///     "message": "",
            ///     "data": {},
            /// }
            actionResult.Result = new JsonResult(JResult.Success(actionData));
        }

        Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] api: '{context.HttpContext.Request.Path}', elapsed: {stopwatch.ElapsedMilliseconds}ms");
    }

    /// <summary>
    /// 判断类和方法头上的特性是否要进行非统一结果返回拦截
    /// </summary>
    /// <param name="actionContext"></param>
    /// <returns></returns>
    private static bool SkipJsonResult(ActionContext actionContext)
    {
        return actionContext.ActionDescriptor.EndpointMetadata.Any(m =>
            m.GetType().FullName == typeof(NoJsonResultAttribute).FullName);
    }

    /// <summary>
    /// 验证返回类型是否满足Object格式
    /// </summary>
    /// <param name="result"></param>
    /// <returns></returns>
    private static (bool isObject, object? data) CheckResult(IActionResult? result)
    {
        return result switch
        {
            ObjectResult objectResult => (true, objectResult.Value),
            JsonResult jsonResult => (true, jsonResult.Value),
            ContentResult contentResult => (false, contentResult.Content),
            _ => (true, null)!, // 如图片流处理
        };
    }
}
